<?php
/**
 * Tree 类实现了高级树形节点
 * 包含childids 和 parentids 的关联更新，方便应用快速的查找子节点与父节点
 *
 * @author
 * @version
 */
class Tree
{
	/**
	 * @var Db
	 */
	protected $Db;

	// 表名
	protected $table;
	// 表主键字段名
	protected $field_id;
	// 表parentid字段名
	protected $field_parentid;
	// 表节点名称字段名
	protected $field_name;
	// 错误信息
	public $error;

	function __construct($table, $id = 'id', $parentid = 'parentid', $name = 'name')
	{
		$this->table = $table;
		$this->field_id = $id;
		$this->field_parentid = $parentid;
		$this->field_name = $name;
		$this->Db = Db::getInstance();
	}

	/**
	 * 根据parentid 获取指定节点的所有子节点
	 * @param null $id
	 * @param string $fields
	 * @param int $deep
	 * @param int $where
	 * @return array
	 */
	public function getChild($id = NULL, $fields = '*', $deep = 0, $where = 1)
	{
		if (is_null($id)) {
			$sql = "SELECT $fields FROM `$this->table` WHERE `$this->field_parentid` is NULL AND $where ORDER BY `sort`";
			$data = $this->Db->query($sql);
		} else {
			$sql = "SELECT $fields FROM `$this->table` WHERE `$this->field_parentid`=$id AND $where ORDER BY `sort`";
			$data = $this->Db->query($sql);
		}
		if ($deep && $data) {
			foreach ($data as $k => $r) {
				$data = array_merge($data, $this->getChild($r[$this->field_id], $fields, $deep));
			}
		}
		return $data;
	}

	/**
	 * 删除指定ID节点
	 * @param $id
	 * @return bool
	 */
	public function rm($id)
	{
		$id = intval($id);
		$r = $this->get($id);
		if (!$r)
			return false;
		$this->Db->query("DELETE FROM `$this->table` WHERE `$this->field_id`=$id");
		if (!is_null($r[$this->field_parentid])) {
			$parentids = explode(',', $r['parentids']);
			array_map(array($this, 'setChildids'), $parentids);
		}
		return true;
	}

	/**
	 * 查找指定ID节点
	 * @param $id
	 * @param string $fields
	 * @return mixed
	 */
	public function get($id, $fields = '*')
	{
		$sql = "SELECT $fields FROM `$this->table` WHERE `$this->field_id`=$id";
        $data = $this->Db->query($sql);
		return $data ? $data[0] : $data;
	}

	/**
	 * 更新指定ID节点的childids字段
	 * @param $id
	 * @return mixed
	 */
	public function setChildids($id)
	{
		$data = $this->getChild($id, '*', 1);
		if ($data) {
			$childids = array();
			foreach ($data as $r) {
				$childids[] = $r[$this->field_id];
			}
			$childids = "'" . implode(',', $childids) . "'";
		}
		else {
			$childids = 'NULL';
		}
		$id = intval($id);
		return $this->Db->query("UPDATE `$this->table` SET `childids`=$childids WHERE `$this->field_id`=$id");
	}

	function set($data)
	{
		if (isset($data[$this->field_parentid]) && (intval($data[$this->field_parentid]) < 1 || $data[$this->field_parentid] == $data[$this->field_id]))
		{
			$data[$this->field_parentid] = NULL;
		}
		unset($data['parentids'], $data['childids']);

		// 修改节点
		if (isset($data[$this->field_id]) && $data[$this->field_id]) {
			$id = intval($data[$this->field_id]);
			unset($data[$this->field_id]);
			$r = $this->get($id);
			if (!$r)
				return false;

			// 修改该节点的树形结构 如移动栏目操作
			$is_update = 0;
			if (array_key_exists($this->field_parentid, $data) && $r[$this->field_parentid] !== $data[$this->field_parentid]) {
				if (isset($data[$this->field_parentid])
					&& in_array($data[$this->field_parentid], explode(',', $r['childids'])))
				{
					$this->error = '逻辑错误';
					return false;
				}
				else
				{
					$is_update = 1;
				}
			}
			$this->Db->update($data,  array('table'=>$this->table, 'where'=>"$this->field_id = $id"));
			if ($is_update) {
				if (array_key_exists($this->field_parentid, $data)) {
					$this->setParentids($id);
					$d = $this->get($id, 'parentids');
					array_map(array($this, 'setChildids'), explode(',', $d['parentids']));
				}
				if (!is_null($r['parentids'])) {
					array_map(array($this, 'setChildids'), explode(',', $r['parentids']));
				}
				if (!is_null($r['childids'])) {
					array_map(array($this, 'setParentids'), explode(',', $r['childids']));
				}
			}
			return true;

		// 新增节点
		} else {
			$result = $this->Db->insert($data, array('table'=>$this->table));
			$id = $this->Db->getLastInsID();
			// 当节点非顶级节点时，需更新当前节点的parentids，并更新当前节点的所有父节点中的childids
			if ($result && isset($data[$this->field_parentid]) && $data[$this->field_parentid] > 0) {
				$this->setParentids($id);
				$r = $this->get($id, 'parentids');
				array_map(array($this, 'setChildids'), explode(',', $r['parentids']));
			}
			return $result ? $id : false;
		}
	}

	/**
	 * 更新指定节点ID的 parentids 字段
	 * @param $id
	 * @return mixed
	 */
	function setParentids($id)
	{
		$data = $this->getParent($id);
		if ($data) {
			foreach ($data as $r) {
				$parentids[] = $r[$this->field_id];
			}
			$parentids = "'" . implode(',', $parentids) . "'";
		} else {
			$parentids = 'NULL';
		}
		$id = intval($id);
		return $this->Db->query("UPDATE `$this->table` SET `parentids`=$parentids WHERE `$this->field_id`=$id");
	}

	/**
	 * 获取指定节点ID的父节点
	 * @param $id
	 * @param string $fields
	 * @param int $is_start
	 * @return array|bool
	 */
	function getParent($id, $fields = '*', $is_start = 1)
	{
		$r = $this->get($id, $fields);
		if (!$r)
			return false;
		if ($is_start) {
			$data = array();
		} else {
			$data[] = $r;
		}
		if (!is_null($r[$this->field_parentid])) {
			$data = array_merge($data, $this->getParent($r[$this->field_parentid], $fields, 0));
		}
		if ($is_start)
			krsort($data);
		return $data;
	}

	/**
	 * 判断指定节点ID是否存在
	 * @param $id
	 * @return bool
	 */
	function exists($id)
	{
		return $this->get($id, $this->field_id) ? true : false;
	}

}